Why Use TCPDF in CodeIgniter 4?
TCPDF remains one of the most popular choices for PDF generation in PHP applications — including CodeIgniter 4 — even in 2026. Here's why thousands of developers prefer it:
- Completely Free & Open-Source: No licensing fees, no restrictions — ideal for both personal and commercial projects.
- Powerful Features: Supports UTF-8 (multilingual text), HTML to PDF conversion, images, barcodes (1D & 2D), digital signatures, page headers/footers, and custom fonts.
- Active Maintenance: Regularly updated by Tecnick.com, compatible with the latest PHP versions (8.2+ and 8.3).
- No External Dependencies: Install via Composer in seconds and use it directly in your controllers.
- Database-Friendly: Easily combine dynamic data from MySQL/PostgreSQL with HTML templates to create personalized invoices, reports, or certificates.
- Proven Track Record: Used in thousands of production applications worldwide for over 15 years.
While newer libraries like Dompdf and mPDF offer better CSS support for complex layouts, TCPDF excels when you need precise control over page elements, barcodes, or when file size and performance matter. For most CodeIgniter 4 projects that require simple yet professional PDFs, TCPDF is still an excellent, reliable choice.
Ready to start? Follow the steps below to integrate TCPDF and generate your first PDF in CodeIgniter 4 today!

Table Of Content
1 Prerequisites
- PHP 8.1+ (CodeIgniter 4 recommends 8.2+ as of 2025/2026)
- Composer installed globally
- MySQL database access
- Basic knowledge of CodeIgniter 4 (controllers, models, views, migrations)
2 Introduction
One of the most reliable and widely used options is TCPDF — a free, open-source PHP library that allows you to create complex PDFs with text, images, tables, barcodes, and more. In this step-by-step tutorial (updated for 2026), you’ll learn how to integrate TCPDF with CodeIgniter 4, connect it to your database, and generate dynamic PDFs from real data. Whether you're building an e-commerce platform, billing system, or admin dashboard, this guide will help you implement professional PDF generation quickly and efficiently.
3 Create / Install a Codeigniter 4 Project
3.1 Install Codeigniter 4 Project
Use the following command to install a new CodeIgniter project:
composer create-project codeigniter4/appstarter ci-4-pdf-app
Then, navigate to your project directory:
cd ci-4-pdf-app
3.2 Configure Environment and MySql Database
# CI_ENVIRONMENT = production
CI_ENVIRONMENT = development
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ci4_pdf
DB_USERNAME=root
DB_PASSWORD=
4 Install TCPDF via Composer
Run the following command.
composer require tecnickcom/tcpdf
Once it is finished, to use it we can directly call it using the use statement in
use TCPDF;
5 Create Models & Migrations
php spark make:model OrderModel
Edit app/Models/OrderModel.php to configure fields for managing order data.
<?php
namespace App\Models;
use CodeIgniter\Model;
class OrderModel extends Model
{
protected $table = 'orders';
protected $primaryKey = 'id';
protected $allowedFields = [
'order_ref', 'invoice_no', 'customer_name', 'customer_address',
'customer_company', 'total_amount', 'status', 'created_at'
];
}
Create a migration file for the orders table:
php spark make:migration CreateOrdersTable
Edit the migration file to define the table structure:
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateOrdersTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'order_ref' => ['type' => 'VARCHAR', 'constraint' => 50],
'invoice_no' => ['type' => 'VARCHAR', 'constraint' => 50],
'customer_name' => ['type' => 'VARCHAR', 'constraint' => 100],
'customer_address' => ['type' => 'TEXT'],
'customer_company' => ['type' => 'VARCHAR', 'constraint' => 100],
'total_amount' => ['type' => 'DECIMAL', 'constraint' => '10,2'],
'status' => ['type' => 'VARCHAR', 'constraint' => 20, 'default' => 'pending'],
'created_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('orders');
}
public function down()
{
$this->forge->dropTable('orders');
}
}
Set up a migration for the Order Items table and a model to manage the data.
php spark make:model OrderItemModel
Edit app/Models/OrderItemModel.php to configure fields for managing order data.
<?php
namespace App\Models;
use CodeIgniter\Model;
class OrderItemModel extends Model
{
protected $table = 'order_items';
protected $primaryKey = 'id';
protected $allowedFields = ['order_id', 'product_name', 'price', 'quantity'];
}
Create a migration file for the order items table:
php spark make:migration CreateOrderItemsTable
Edit the migration file to define the table structure:
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateOrderItemsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'order_id' => ['type' => 'INT', 'unsigned' => true],
'product_name' => ['type' => 'VARCHAR', 'constraint' => 100],
'price' => ['type' => 'DECIMAL', 'constraint' => '10,2'],
'quantity' => ['type' => 'INT'],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('order_items');
}
public function down()
{
$this->forge->dropTable('order_items');
}
}
Run the migration:
php spark migrate
6 Build the Invoice Controller
php spark make:controller InvoiceController
Add methods for generating PDF:
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use App\Models\OrderModel;
use App\Models\OrderItemModel;
use TCPDF;
class InvoiceController extends BaseController
{
protected $orderModel;
protected $orderItemModel;
public function __construct()
{
$this->orderModel = new OrderModel();
$this->orderItemModel = new OrderItemModel();
}
public function index()
{
$data['orders'] = $this->orderModel->findAll();
return view('orders', $data);
}
public function generate($id)
{
$order = $this->orderModel->find($id);
if (!$order) {
return redirect()->to('/orders')->with('error', 'Order not found');
}
$items = $this->orderItemModel->where('order_id', $id)->findAll();
$html = view('invoice_pdf', [
'order' => $order,
'items' => $items
]);
$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false);
$pdf->SetMargins(15, 15, 15);
$pdf->SetAutoPageBreak(true, 15);
$pdf->SetCreator('My App');
$pdf->SetAuthor('Your Name');
$pdf->SetTitle('Invoice #' . $order['invoice_no']);
$pdf->SetSubject('Invoice');
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->AddPage();
$pdf->writeHTML($html, true, false, true, false, '');
$this->response->setContentType('application/pdf');
$pdf->Output('invoice_' . $order['invoice_no'] . '.pdf', 'I'); // 'I' = inline, 'D' = download
}
}
?>
7 Create Views (List + PDF Template)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Orders - PDF Generation</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container mt-5">
<h1>Order List</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Order Ref</th>
<th>Invoice No</th>
<th>Customer</th>
<th>Total</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<tr>
<td><?= esc($order['order_ref']) ?></td>
<td><?= esc($order['invoice_no']) ?></td>
<td><?= esc($order['customer_name']) ?></td>
<td>$<?= number_format($order['total_amount'], 2) ?></td>
<td><?= esc($order['status']) ?></td>
<td>
<a href="<?= site_url('invoice/' . $order['id']) ?>" target="_blank" class="btn btn-primary btn-sm">View Invoice</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</body>
</html>
Create an invoice_pdf.php file in app/Views (PDF Template – Use inline styles & tables).
<!DOCTYPE html>
<html>
<head>
<title>Invoice #<?= esc($order['invoice_no']) ?></title>
<style>
body { font-family: Arial, Helvetica, sans-serif; font-size: 12pt; }
h1 { color: #333; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ddd; padding: 8px; }
th { background-color: #f2f2f2; text-align: left; }
.text-right { text-align: right; }
.total { font-weight: bold; font-size: 14pt; }
</style>
</head>
<body>
<h1>Invoice</h1>
<hr>
<table>
<tr>
<td><strong>Invoice No:</strong> <?= esc($order['invoice_no']) ?></td>
<td class="text-right"><strong>Date:</strong> <?= date('d/m/Y', strtotime($order['created_at'])) ?></td>
</tr>
<tr>
<td><strong>Customer:</strong> <?= esc($order['customer_name']) ?></td>
<td class="text-right"><strong>Due Date:</strong> <?= date('d/m/Y', strtotime($order['created_at'] . ' +10 days')) ?></td>
</tr>
<tr>
<td><?= esc($order['customer_company']) ?><br><?= nl2br(esc($order['customer_address'])) ?></td>
<td></td>
</tr>
</table>
<br>
<table>
<thead>
<tr>
<th>Product</th>
<th class="text-right">Price</th>
<th class="text-right">Qty</th>
<th class="text-right">Subtotal</th>
</tr>
</thead>
<tbody>
<?php $total = 0; foreach ($items as $item):
$subtotal = $item['price'] * $item['quantity'];
$total += $subtotal;
?>
<tr>
<td><?= esc($item['product_name']) ?></td>
<td class="text-right">$<?= number_format($item['price'], 2) ?></td>
<td class="text-right"><?= $item['quantity'] ?></td>
<td class="text-right">$<?= number_format($subtotal, 2) ?></td>
</tr>
<?php endforeach; ?>
<tr class="total">
<td colspan="3" class="text-right">Total</td>
<td class="text-right">$<?= number_format($total, 2) ?></td>
</tr>
</tbody>
</table>
</body>
</html>
8 Define a Route
get('/', 'Home::index');
$routes->get('/orders', 'InvoiceController::index');
$routes->get('/invoice/(:num)', 'InvoiceController::generate/$1');
9 Add Sample Data (Optional Seeder)
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class OrderSeeder extends Seeder
{
public function run()
{
$orderData = [
'order_ref' => 'ORD-001',
'invoice_no' => 'INV-001',
'customer_name' => 'John Doe',
'customer_address' => "123 Main St\nLos Angeles, CA",
'customer_company' => 'Acme Corp',
'total_amount' => 250.00,
'status' => 'paid',
'created_at' => date('Y-m-d H:i:s')
];
$this->db->table('orders')->insert($orderData);
$orderId = $this->db->insertID();
$items = [
['order_id' => $orderId, 'product_name' => 'Widget A', 'price' => 100.00, 'quantity' => 1],
['order_id' => $orderId, 'product_name' => 'Widget B', 'price' => 50.00, 'quantity' => 3],
];
$this->db->table('order_items')->insertBatch($items);
}
}
?>
Run: php spark db:seed OrderSeeder
10 Folder Structure
11 Run and Test the Application
php spark serve
Visit: http://localhost:8080/orders
Click "View Invoice" → Opens PDF in new tab. 12 Conclusion
Congratulations! You've now successfully learned how to generate PDF files in CodeIgniter 4 using TCPDF. From installing the library via Composer to fetching dynamic data from your database, rendering HTML views, and outputting professional-looking PDFs (like invoices or reports), this tutorial provides a complete, production-ready foundation.
TCPDF stands out for its reliability, support for UTF-8/multilingual text, barcodes, and precise page control — making it an excellent choice for many CodeIgniter 4 applications in 2026. By using inline CSS and table-based layouts in your views, you can achieve clean, consistent results despite TCPDF's limited advanced CSS support.
While TCPDF works great for structured documents, explore modern alternatives like Dompdf (better HTML/CSS rendering) or mPDF (strong Unicode and complex layout support) if your needs evolve toward more design-heavy PDFs.
Implement this in your next project — whether it's an e-commerce billing system, admin dashboard, or reporting tool — and deliver polished, downloadable documents to your users effortlessly. Happy coding, and feel free to adapt the code for receipts, certificates, or any custom PDF generation task!
Ready to build more? Check out our other CodeIgniter 4 tutorials or drop a comment below with your questions.
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
You need CodeIgniter 4 installed via Composer, PHP 8.1+, a configured MySQL database in .env, and basic knowledge of models, migrations, controllers, and views.
Run the command: composer require tecnickcom/tcpdf. Then, use it in your controller with 'use TCPDF;'.
In your controller, load data (e.g., order and items from models), render the view to HTML with view('invoice', $data)->render(), create a new TCPDF instance, configure it, add a page, use writeHTML($html), and output the PDF.
Set the response content type: $this->response->setContentType('application/pdf'); Then use $pdf->Output('invoice.pdf', 'I') for inline display or 'D' for forced download.
TCPDF has limited CSS support. Use inline styles directly in your view's HTML (e.g., style attributes on tables, divs) for reliable formatting like borders, colors, and fonts.
Fetch data in the controller using models (e.g., OrderModel->find($id) and OrderitemModel->where('order_id', $id)->findAll()), pass it to the view, and loop through items to build tables with calculations in the HTML.
When instantiating TCPDF: new TCPDF('L' for landscape, PDF_UNIT, 'A5' or other format). Use methods like SetCreator(), SetAuthor(), SetTitle(), and setPrintHeader(false)/setPrintFooter(false) to disable defaults.
Common causes: Unsupported CSS (use inline only), HTML structure errors, or encoding issues. Test with simple HTML, ensure UTF-8, and check for PHP errors in the controller.
Yes, include tags in your view HTML with absolute paths (e.g., base_url('assets/logo.png')) or base64-encoded data for better compatibility.
Yes, it handles tables, text, calculations, and basic layouts well for invoices. For more advanced HTML/CSS fidelity, consider alternatives like Dompdf.
